package org.game.actor;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.base.BaseGoods;
import org.bean.AccountInfo;
import org.cache.GoodsCfgCache;
import org.cfg.ItemRecoverCfg;
import org.cfg.ItemTimeCfg;
import org.cfg.cache.ItemRecoverCfgCache;
import org.constant.EventConditionKey;
import org.constant.EventConditionType;
import org.constant.EventType;
import org.constant.ItemType;
import org.error.GameErrorCode;
import org.game.actor.msg.ClientMsg;
import org.game.actor.msg.InternalMsg;
import org.game.constant.BuildingConstant;
import org.game.constant.SecondsJobStatus;
import org.game.constant.SessionConstent;
import org.game.db.bean.Building;
import org.game.db.bean.ComposeSummon;
import org.game.db.bean.Item;
import org.game.db.bean.Player;
import org.game.db.bean.PlayerData;
import org.game.db.cache.ItemCache;
import org.game.db.vo.BackpackVO;
import org.game.db.vo.PlayerDataVO.ResBuyRecordVO;
import org.game.log.LogGoods;
import org.game.log.LogPlayerAction;
import org.game.log.builder.LogDsp;
import org.game.log.dsp.GoodsDsp;
import org.game.log.dsp.PlayerActionDsp;
import org.game.manager.BuildingManager;
import org.game.manager.ItemManager;
import org.game.manager.OfflineRewardManager;
import org.game.manager.PlayerManager;
import org.game.module.Dispatcher;
import org.game.module.core.Core;
import org.game.module.itembo.ItemBO;
import org.game.module.summonbo.SummonBO;
import org.game.msgBuilder.ItemMsgBuilder;
import org.game.msgBuilder.PlayerMsgBuilder;
import org.game.msgBuilder.ResourceLogMsgBuilder;
import org.game.msgBuilder.SummonMsgBuilder;
import org.game.plugin.event.GameEvent;
import org.game.plugin.event.GameEventPlugin;
import org.game.protobuf.s2c.S2CItemMsg.ItemList;
import org.game.protobuf.s2c.S2CLoginMsg.EnterSuc;
import org.game.protobuf.s2c.S2CPlayerMsg.RespBuyResourcesLog;
import org.game.protobuf.s2c.S2CShareMsg.ChangeType;
import org.game.util.ProtoUnit;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.sojoys.artifact.constant.ArtifactErrorCode;
import com.sojoys.artifact.exception.AbstractLogicModelException;
import com.sojoys.artifact.factory.message.IMessage;
import com.sojoys.artifact.factory.session.ISession;
import com.sojoys.artifact.manager.MessageManager;
import com.sojoys.artifact.tools.ToolDate;
import com.sojoys.artifact.tools.ToolError;
import com.sojoys.artifact.tools.ToolMap;
import com.xiaoleilu.hutool.date.DateUtil;
import com.xiaoleilu.hutool.log.Log;
import com.xiaoleilu.hutool.log.LogFactory;

import akka.actor.AbstractActor;
import akka.japi.pf.ReceiveBuilder;

/**
 * @author : DengYing
 * @CreateDate : 2017年12月15日 下午1:10:33
 * @Description ：Please describe this document
 */
public class PlayerActor extends AbstractActor {
	
	static final Log log = LogFactory.get(PlayerActor.class);
	
	private Player player = null;

	public PlayerActor(Player player) {
		this.player = player;
	}

	@Override
	public Receive createReceive() {
		// TODO Auto-generated method stub
		return ReceiveBuilder.create().match(ClientMsg.class, clientMsg -> {
			Player player = clientMsg.getSession().getAttr(SessionConstent.PLAYER);
			// 登陆检查
			ToolError.isAndTrue(GameErrorCode.NOT_LOGIN, player == null);
			
			Dispatcher.me().disp(clientMsg.getSession(), clientMsg.getMsg());
		}).match(InternalMsg.class, internalMsg -> {
			try {
				ImmutableMap<String, Object> msg = internalMsg.getMsg();

				switch (internalMsg.getCommandId()) {
				case "online": {
					// 玩家上线
					ISession session = ToolMap.getObject("session", msg, ISession.class);
					String type = ToolMap.getString("type", msg,"login");
					
					IMessage out = MessageManager.me().create();
					switch (type) {
					case "login":
						out.setCommandId(Core.enterGame);
						break;
					case "reconnect":
						out.setCommandId(Core.reconnect);
						break;
					}
					
					try {
						
						// 执行上限
						PlayerManager.me().online(session, player);
						AccountInfo accountInfo = player.getPlayerProxy().getAccountInfo();
						String ip = session.remoteAddress().toString();
						String clientVersion =  accountInfo.getClientVersion();
						String imei =  accountInfo.getImei();
						String device =  accountInfo.getDevice();
						String osVersion =  accountInfo.getOsVersion();
						switch (type) {
						case "login":
							
							LogPlayerAction.me().loginLog(player.getId(), 0, ip,clientVersion,imei,device,osVersion, LogDsp.newBuilder(PlayerActionDsp.LOGIN));
							EnterSuc.Builder builder = EnterSuc.newBuilder();
							// 玩家信息
							builder.setPlayerinfo(PlayerMsgBuilder.createPlayerInfo(player));
							builder.setServerTime((int) (System.currentTimeMillis() / 1000));
							// 发送消息
							player.getPlayerProxy().sendMsg(MessageManager.me().create(Core.enterGame, ProtoUnit.toByte(builder.build())));

//						Thread.sleep(3000);
//						session.close();
							break;
						case "reconnect":
							LogPlayerAction.me().loginLog(player.getId(), 0,ip,clientVersion,imei,device,osVersion, LogDsp.newBuilder(PlayerActionDsp.RECONNECT));
							session.sendMessage(MessageManager.me().create(Core.reconnect,new byte[0]));
							break;
						}
						// 线下奖励
						OfflineRewardManager.me().pullRewardInfo(player);
						break;
					} catch (AbstractLogicModelException e) {
						out.setStatus(e.getCode());
						session.sendMessage(out);
					} catch (Exception e) {
						log.error(e);
						out.setStatus(ArtifactErrorCode.UNKNOWN_ERROR);
						session.sendMessage(out);
					} 
				}
				case "offline":{
					PlayerManager.me().offline(player);
					break;
				}
				case "SecondsJob": {
					// 修改状态为运行中
					player.getPlayerProxy().setSecondsJobStatus(SecondsJobStatus.EXECUTE);
					// 每秒定时
					List<ItemRecoverCfg> recovers = ItemRecoverCfgCache.me().getAll();

					Date nowDate = new Date();
					// 资源恢复
					autoRecoverItem(recovers, nowDate);
					// 重置资源购买次数
					resetBuyResources(recovers, nowDate);
					// 建筑升级
					buildingJob(nowDate);
					// 删除过期物品
					goodsCountDown(nowDate);
	                //召唤合成
	                checkSummonCompose(nowDate);

					// 如果没有激活任务就暂停
					if (player.getPlayerProxy().getSecondsJobStatus() == SecondsJobStatus.EXECUTE) {
						player.getPlayerProxy().setSecondsJobStatus(SecondsJobStatus.PAUSE);
					}
					break;
				}
				default:
					break;
				}
			} catch (Exception e) {
				log.error(e);
			}
		}).build();
	}

	/**
	 * 重置资源购买次数
	 * @param recovers
	 * @param nowDate
	 */
	private void resetBuyResources(List<ItemRecoverCfg> recovers, Date nowDate) {
		PlayerData playerData = player.getPlayerDatasFkPlayerId();
		
		Map<Integer, ResBuyRecordVO> buyRecords = playerData.getPlayerDataProxy().getResBuyRecordVO();
		if (buyRecords.isEmpty()) {
			return;
		}
		Set<Integer> resetKeys = new HashSet<>();

		RespBuyResourcesLog.Builder resp = RespBuyResourcesLog.newBuilder();

		buyRecords.forEach((k,v)->{
			// 第一次购买时间
			long firstBuyTime = v.getTime();

//			int buyCount = Integer.valueOf(split.get(1));

			ItemRecoverCfg cfg = ItemRecoverCfgCache.me().getById(k);
			if (cfg==null) {
				return;
			}
			int resetTime = (int) (cfg.getResetBuyCountTime() * ToolDate.TIME_SECOND);

			if (resetTime == 0) {
				return;
			}

			boolean reset = true;

			//resetTime 小于0就是跨天刷新
			if (resetTime < 0){
				if (DateUtil.betweenDay(new Date(firstBuyTime), nowDate, true)==0) {
					player.getPlayerProxy().setNextSecondsJobTime(DateUtil.endOfDay(nowDate).getTime());
					reset &= false;
				}else {
					reset &= true;
				}
			}else{
				long runTime = firstBuyTime + resetTime;
				if (nowDate.getTime() >= runTime) {
					reset &= true;
				}else {
					player.getPlayerProxy().setNextSecondsJobTime(runTime);
					reset &= false;
				}
			}

			if (reset) {
				resetKeys.add(k);
				resp.addLogs(ResourceLogMsgBuilder.buildResourceLogMsg(ChangeType.DELETE, k, 0));
			}
		});

		if (!resetKeys.isEmpty()) {
			buyRecords.keySet().removeAll(resetKeys);
			playerData.update();

			IMessage outMsg = MessageManager.me().create(Core.buyResourcesLog, ProtoUnit.toByte(resp.build()));
			player.getPlayerProxy().sendMsg(outMsg);
		}
	}

	/**
	 * 玩家道具恢复
	 *
	 * @param recovers
	 * @param nowDate
	 */
	private void autoRecoverItem(List<ItemRecoverCfg> recovers, Date nowDate) {

		PlayerData playerData = player.getPlayerDatasFkPlayerId();
		Map<Integer, Long> autoRecover = playerData.getPlayerDataProxy().getAutoRecover();
		if (autoRecover.isEmpty()) {
			return;
		}
		Map<Integer, Integer> res = Maps.newHashMap();
		for (ItemRecoverCfg itemRecoverCfg : recovers) {
			if (!autoRecover.containsKey(itemRecoverCfg.getItemId())) {
				continue;
			}
			BaseGoods baseGoods = GoodsCfgCache.me().get(itemRecoverCfg.getItemId());
			BackpackVO backpackVO = player.getPlayerProxy().getBackpackByType(baseGoods.getBagType());

			// 取得最大恢复上限
			int max_count = ItemManager.getMaxAutoRecoverCount(player, itemRecoverCfg);

			int time_interval = (int) (itemRecoverCfg.getCooldown() * ToolDate.TIME_SECOND);

			if (time_interval == 0) {
				continue;
			}


			// 取得上次增长时间
			long lastRecoverTime = autoRecover.get(itemRecoverCfg.getItemId());

			int nowNum = backpackVO.countByCfg(baseGoods);

			boolean canRefresh = true;

			//time_interval 小于0就是跨天刷新
			if (time_interval < 0){
				if (DateUtil.betweenDay(new Date(lastRecoverTime), nowDate, true)==0) {
					player.getPlayerProxy().setNextSecondsJobTime(DateUtil.endOfDay(nowDate).getTime());
					canRefresh &= false;
				}else {
					canRefresh &= true;
				}
			}else{
				long runTime = lastRecoverTime + time_interval;
				if (nowDate.getTime() >= runTime) {
					canRefresh &= true;
				}else {
					player.getPlayerProxy().setNextSecondsJobTime(runTime);
					canRefresh &= false;
				}
			}
//			canRefresh &= nowNum < max_count;



			if (canRefresh) {
				int addCount = itemRecoverCfg.getRecoverCount();
				if (time_interval > 0){
					addCount = (int) (((nowDate.getTime() - lastRecoverTime) / time_interval) * itemRecoverCfg.getRecoverCount());
				}
				// 真实恢复数量
				addCount = Math.min(max_count - nowNum, addCount);
				if (addCount <= 0) {
					// 如果恢复已达最大恢复数量，就停止恢复
					autoRecover.remove(itemRecoverCfg.getItemId());
				}else {
					res.put(itemRecoverCfg.getItemId(), addCount);
					autoRecover.put(itemRecoverCfg.getItemId(), nowDate.getTime());
				}
			}
		}
		playerData.update();

		if (!res.isEmpty()) {
			LogDsp log = LogDsp.newBuilder(GoodsDsp.AUTO_RECOVER);
			
			ItemManager.addGoodsAndPush(player, null, null, res, log);
		}
	}

	/**
	 * 建筑
	 *
	 * @param nowDate
	 *            当前时间
	 */
	private void buildingJob(Date nowDate) {
		List<Building> buildingList = player.getBuildingsFkPlayerId();
		if (buildingList == null || buildingList.size() == 0) {
			return;
		}
		buildingList.forEach(building -> {
			if (building.getFinishTime() == null)
				return;
			switch (building.getState()) {
			case BuildingConstant.BUIDING_STATE_UPGRADE:
				if (building.getFinishTime().getTime() <= nowDate.getTime()) {
					BuildingManager.buildingUpgrade(player, building);
				}else {
					player.getPlayerProxy().setNextSecondsJobTime(building.getFinishTime().getTime());
				}
				break;
			case BuildingConstant.BUIDING_STATE_WORK:

				if (building.getNextRewardTime().getTime() <= nowDate.getTime()) {
					
					Map<String, Object> in = Maps.newHashMap();
					in.put(EventConditionKey.CONDITION_TYPE, EventConditionType.WORK_STAGE);
					in.put(EventConditionKey.BUILD_CID, building.getCid());
					in.put(EventConditionKey.BUILD_NUMBER, building.getBuildingProxy().getBuildId());
					in.put(EventConditionKey.BUILD_LVL, building.getBuildingProxy().getCfg().getLevel());
					GameEvent event = new GameEvent(player, EventType.BUILDING_EVENT, in);
					GameEventPlugin.syncSubmit(event);

					ToolDate.addSecond(building.getBuildingProxy().getCfg().getCycle(), building.getNextRewardTime());
					building.update();
					
					player.getPlayerProxy().startSecondsJob();
				}else {
					player.getPlayerProxy().setNextSecondsJobTime(building.getNextRewardTime().getTime());
				}

				if (building.getFinishTime().getTime() <= nowDate.getTime()) {
					BuildingManager.workFinish(player, building);
				}else {
					player.getPlayerProxy().setNextSecondsJobTime(building.getFinishTime().getTime());
				}
				break;
			default:
				break;
			}
		});
	}

	/**
	 * 删除玩家过期物品
	 */
	private void goodsCountDown(Date nowDate) {
		List<Item> itemList = player.getItemsFkPlayerId();
		List<Item> outTimeGoods = Lists.newArrayList();

		ItemList.Builder builder = ItemList.newBuilder();
		for (Item item : itemList) {
			BaseGoods goods = item.getItemProxy().getCfg();
			if (goods == null || !(goods instanceof ItemTimeCfg)) {
				continue;
			}

			ItemTimeCfg cfg = (ItemTimeCfg) goods;
			long outTime = 0;
			switch (cfg.getLimitType()) {
			case ItemType.DYNAMIC_TIME_LIMIT:
				outTime = item.getCreateDate().getTime() + ToolDate.TIME_SECOND * cfg.getLimitData();
				break;
			case ItemType.FIXED_TIME_LIMIT:
				outTime = cfg.getLimitData();
				break;
			}

			if (nowDate.getTime() > outTime) {
				outTimeGoods.add(item);
				ItemMsgBuilder.packageItemInfo(ChangeType.DELETE, builder, item);
				
				LogDsp log = LogDsp.newBuilder(GoodsDsp.TIME_OUT_DELETE);
				// 日志记录
				LogGoods.me().log(player.getId(), item.getCid(), -item.getNum(), 0, log);
			}else {
				player.getPlayerProxy().setNextSecondsJobTime(outTime);
			}
		}
		if (!outTimeGoods.isEmpty()) {
			player.getPlayerProxy().sendMsg(MessageManager.me().create(ItemBO.getItems, ProtoUnit.toByte(builder.build())));
			ItemCache.me().deleteBatch(outTimeGoods);
		}
	}

	private void checkSummonCompose(Date nowDate){
		List<ComposeSummon> all = player.getComposeSummonsFkPlayerId();
		List<ComposeSummon> composeFinishList = Lists.newArrayList();
		long nowTime = nowDate.getTime();
		for (ComposeSummon composeSummon : all) {
			if (composeSummon.getFinishTime().getTime() <= nowTime){
				if (!composeSummon.getComposeSummonProxy().isPushedComposeOverMsg()){
					composeFinishList.add(composeSummon);
					composeSummon.getComposeSummonProxy().setPushedComposeOverMsg(true);
				}
			}else{
				player.getPlayerProxy().setNextSecondsJobTime(composeSummon.getFinishTime().getTime());
			}
		}
		if (composeFinishList.size() > 0) {
			player.getPlayerProxy().sendMsg(SummonBO.noticeComposeFinish, SummonMsgBuilder.buildNoticeComposeFinishMsg(composeFinishList));
		}
	}
}
